/*!
* @file Vector2d.h
* @brief 二维矢量（点）
* @date 2024/9/11
*/
#pragma once

#include <cmath>
#include <cfloat>
#include <limits>
#include <ostream>
#include "DataDef.h"

namespace data
{
/*!
* @brief 二维矢量（点）
*/
template<class float_type>
class Vector2
{
public:
    using traits_type = std::numeric_limits<float_type>;

    /*!
    * @brief 开放成员
    */
    float_type x;
    float_type y;

    /*!
    * @brief 矢量构造
    */
    Vector2(float_type fx = 0.0, float_type fy = 0.0) : x(fx), y(fy) {}
    template<typename R>
    explicit Vector2(const Vector2<R>& v) : x(v.x), y(v.y) {}
    Vector2(const Vector2&) = default;
    Vector2(Vector2&&) = default;
    ~Vector2() = default;

    /*!
    * @brief 矢量运算
    */
    Vector2& operator=(const Vector2&) = default;
    Vector2& operator=(Vector2&&) = default;
    bool operator==(const Vector2& v) const;
    bool operator!=(const Vector2& v) const;
    Vector2 operator+() const;
    Vector2 operator+(const Vector2& v) const;
    Vector2& operator+=(const Vector2& v);
    Vector2 operator-() const;
    Vector2 operator-(const Vector2& v) const;
    Vector2& operator-=(const Vector2& v);
    Vector2 operator*(float_type c) const;
    Vector2& operator*=(float_type c);
    float_type operator*(const Vector2& v) const;
    Vector2 operator/(float_type c) const;
    Vector2& operator/=(float_type c);

    /*!
    * @brief 矢量操作
    */
    float_type Length() const;
    float_type Angle() const;
    float_type Sqr() const;
    Vector2& Set(float_type x, float_type y);
    Vector2& Negate();
    Vector2& Scale(float_type factor);
    Vector2& Rotate(float_type angle);
    Vector2& Normalize();

    bool IsZero() const;
    float_type Distance(const Vector2& v) const;
    bool IsEqual(const Vector2& v, float_type tol = 0.0) const;
    float_type GetAngle(const Vector2& vec) const;

    /*!
    * @brief 类型转换
    */
    template<typename R>
    operator Vector2<R>() const { return Vector2<R>(x, y); }

    /*!
    * @brief 文本信息输出
    */
    friend std::ostream& operator<< (std::ostream& stream, const Vector2& v)
    {
        return stream << "{ x: " << v.x << ", y: " << v.y << " }";
    }
};

using Vector2f = Vector2<float>;
using Vector2d = Vector2<double>;
};
